using System;
using System.Linq;
using System.Collections.Generic;
using Headsight.BPMN2WF.BPMN.Schemas;
using Headsight.BPMN2WF.Testing.TestObjCtors;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Headsight.BPMN2WF.BPMN.Model
{
    [TestClass]
    public class BPMNProcessTest : BPMNBaseElementTestFixture<BPMNFlowNodesBlock, tFlowNode>
    {
        private tProcessCtor _tProcCtor;
        private BPMNFlowNode _tFlowNode1;
        private BPMNFlowNode _tFlowNode2;
        private BPMNSequenceFlow _tSeqFlow1;
        private BPMNSequenceFlow _tSeqFlow2;
        private IList<BPMNArgument> _bpmnArgs;

        [TestInitialize]
        public void SetUp()
        {
        }

        protected void CreateSut(tProcess tProcess)
        {
            _sut = new BPMNFlowNodesBlockFactory().CreateBPMNFlowNodesBlock(
                new tDefinitionsCtor().With(tProcess).Create());
        }

        protected override tFlowNode CreateTElement()
        {
            return new tFlowNodeCtor().Create();
        }

        protected override BPMNFlowNodesBlock CreateSut(tFlowNode tProcess, string expectedId)
        {
            _tProcCtor = new tProcessCtor();

            _tFlowNode1 = new BPMNFlowNodeCtor().WithId("_id1").Create();
            _tFlowNode2 = new BPMNFlowNodeCtor().WithId("_id2").Create();
            _tSeqFlow1 = new BPMNSequenceFlowCtor().WithId("_id3").Create();
            _tSeqFlow2 = new BPMNSequenceFlowCtor().WithId("_id4").Create();

            _bpmnArgs = new List<BPMNArgument>();

            tProcess.id = expectedId;

            var flowNode = new BPMNFlowNodeCtor().WithTFlowElement(tProcess).WithId(expectedId).Create();
            var bpmnFlowNodes = new Dictionary<BPMNFlowNode, IList<BPMNFlowNode>>();
            var bpmnSequenceFlows = new Dictionary<String, BPMNSequenceFlow>();
            
            bpmnFlowNodes[_tFlowNode1] = new List<BPMNFlowNode>();
            bpmnFlowNodes[_tFlowNode2] = new List<BPMNFlowNode>();
            bpmnSequenceFlows[_tSeqFlow1.Id] = _tSeqFlow1;
            bpmnSequenceFlows[_tSeqFlow2.Id] = _tSeqFlow2;

            return new BPMNFlowNodesBlock(flowNode, _bpmnArgs,
                                          bpmnFlowNodes,
                                          bpmnSequenceFlows);
        }

        private void CreateSut(List<BPMNFlowNode> bpmnFlowNodes)
        {
            bpmnFlowNodes.ForEach(x => _tProcCtor.With(x.TElement));

            _sut = new BPMNFlowNodesBlockFactory().CreateBPMNFlowNodesBlock(
                new tDefinitionsCtor().With(_tProcCtor.Create()).Create()
                );
        }

        [TestMethod]
        public override void TestToString()
        {
            const String exp = "BPMNFlowNodesBlock[Id: block_**id**, Name: ]";
            Assert.AreEqual(exp, _sut.ToString());
        }

        [TestMethod]
        public void FullQualifiedName()
        {
            CreateSut(_tProcCtor.WithStartEvent("start").WithId("TypeName").Create());
            Assert.AreEqual("TypeName", _sut.FullQualifiedName);

            CreateSut(_tProcCtor.WithStartEvent("start").WithId("Namespace1.Namespace2.TypeName").Create());
            Assert.AreEqual("Namespace1.Namespace2.TypeName", _sut.FullQualifiedName);

            CreateSut(_tProcCtor.WithStartEvent("start").WithId("typeName").Create());
            Assert.AreEqual("TypeName", _sut.FullQualifiedName);

            CreateSut(_tProcCtor.WithStartEvent("start").WithId("namespace1.namespace2.typeName").Create());
            Assert.AreEqual("Namespace1.Namespace2.TypeName", _sut.FullQualifiedName);
        }

        [TestMethod]
        public void BPMNFlowNodes()
        {
            var actual = _sut.BPMNFlowNodes;

            Assert.IsNotNull(actual);
            Assert.AreEqual(2, actual.Count);
            Assert.AreSame(_tFlowNode1, actual[0]);
            Assert.AreSame(_tFlowNode2, actual[1]);
        }

        [TestMethod]
        public void BPMNSequenceFlows()
        {
            var actual = _sut.BPMNSequenceFlows;

            Assert.IsNotNull(actual);
            Assert.AreEqual(2, actual.Count);
            Assert.AreSame(_tSeqFlow1, actual[0]);
            Assert.AreSame(_tSeqFlow2, actual[1]);
        }

        [TestMethod]
        public void GetBPMNFlowNodeById()
        {
            Assert.AreSame(_tFlowNode1, _sut.GetBPMNFlowNodeById(_tFlowNode1.Id));
            Assert.AreSame(_tFlowNode2, _sut.GetBPMNFlowNodeById(_tFlowNode2.Id));
        }

        [TestMethod]
        public void GetBPMNSequenceFlowById()
        {
            Assert.AreSame(_tSeqFlow1, _sut.GetBPMNSequenceFlowById(_tSeqFlow1.Id));
            Assert.AreSame(_tSeqFlow2, _sut.GetBPMNSequenceFlowById(_tSeqFlow2.Id));
        }

        [TestMethod]
        public void StartProcess()
        {
            var bpmnFlowNodes = new List<BPMNFlowNode>
            {
                new BPMNFlowNodeCtor().WithId("1").WithTFlowElement(new tStartEvent() { id = "1"})
                .WithNumBPMNSequenceFlowsIn(1).Create(),
                new BPMNFlowNodeCtor().WithId("2").WithNumBPMNSequenceFlowsIn(2).Create(),
                new BPMNFlowNodeCtor().WithId("3").WithNumBPMNSequenceFlowsIn(0).Create()
            };
            CreateSut(bpmnFlowNodes);

            var actual = _sut.FlowNodeIn;
            var expected = _sut.GetBPMNFlowNodeById("1");
            Assert.AreSame(expected, actual);
        }

        [TestMethod]
        public void GetTargetBPMNFlowNodes_in_case_of_no_outgoing_BPMNSequenceFlows()
        {
            var bpmnFlowNodes = new List<BPMNFlowNode>
            {
                new BPMNFlowNodeCtor().WithId("0").Create()
            };
            CreateSut(bpmnFlowNodes);

            var actual = _sut.GetTargetBPMNFlowNodes(bpmnFlowNodes[0]);

            Assert.IsNotNull(actual);
            Assert.AreEqual(0, actual.Count);
        }

        [TestMethod]
        public void GetTargetBPMNFlowNodes_in_case_of_1_outgoing_BPMNSequenceFlows()
        {
            CreateSut(new tProcessCtor().Flow("0", "1").Create());

            var actual = _sut.GetTargetBPMNFlowNodes("0");

            Assert.AreEqual(1, actual.Count);
            Assert.AreSame(_sut.GetBPMNFlowNodeById("1"), actual[0]);
        }

        [TestMethod]
        public void GetTargetBPMNFlowNodes_in_case_of_2_outgoing_BPMNSequenceFlows()
        {
            CreateSut(new tProcessCtor().Flow("0", "1").Flow("0", "2").Create());

            var actual = _sut.GetTargetBPMNFlowNodes("0");

            Assert.AreEqual(2, actual.Count);
            Assert.AreEqual(_sut.GetBPMNFlowNodeById("1"), actual[0]);
            Assert.AreSame(_sut.GetBPMNFlowNodeById("2"), actual[1]);
        }

        [TestMethod]
        public void Arguments()
        {
            Assert.AreSame(_bpmnArgs, _sut.Arguments);
        }

        private void Perform_NumBPMNFlowNodesTest<T>(int expected, params ITestObjCtor<T>[] tFlowElementCtors) where T : tFlowElement
        {
            var tProcessCtor = new tProcessCtor();
            foreach (var tFlowElementCtor in tFlowElementCtors)
                tProcessCtor.With(tFlowElementCtor);

            var tProcess = tProcessCtor.Create();
            CreateSut(tProcess);

            var actual = _sut.NumBPMNFlowNodes;

            Assert.AreEqual(expected, actual);
        }

        [TestMethod]
        public void NumBPMNFlowNodes_in_case_of_tProcess_with_1_tFlowNode()
        {
            Perform_NumBPMNFlowNodesTest(1, new tFlowNodeCtor().WithId("_id1"));
        }

        [TestMethod]
        public void NumBPMNFlowNodes_in_case_of_tProcess_with_2_tFlowNode()
        {
            Perform_NumBPMNFlowNodesTest(2, new tFlowNodeCtor().WithId("_id1"), new tFlowNodeCtor().WithId("_id2"));
        }

        [TestMethod]
        public void GetTargetBPMNFlowNodes()
        {
            CreateSut(new tProcessCtor().Flow("_id1", "_id2").Create());

            var result1 = _sut.GetTargetBPMNFlowNodes("_id1");
            Assert.AreEqual(1, result1.Count);
            Assert.AreEqual(true, result1.Contains(_sut.GetBPMNFlowNodeById("_id2")));

            var result2 = _sut.GetTargetBPMNFlowNodes("_id2");
            Assert.AreEqual(0, result2.Count);
        }

        [TestMethod]
        public void GetTargetBPMNFlowNodes_in_case_of_no_such_vertex()
        {
            CreateSut(new tProcessCtor().Flow("_id1", "_id2").Create());

            try
            {
                _sut.GetTargetBPMNFlowNodes("no_such_id");
                Assert.Fail("**failed**");
            }
            catch (Exception e)
            {
                const String exp = "Id: no_such_id";
                Assert.AreEqual(exp, e.Message);
            }
        }

        [TestMethod]
        public void GetTargetBPMNFlowNodes2()
        {
            CreateSut(new tProcessCtor().WithStartEvent("start").Flow("start", "_id1")
                .Flow("_id1", "_id2")
                .Flow("_id1", "_id3")
                .Flow("_id3", "_id1").Create());

            var result1 = _sut.GetTargetBPMNFlowNodes("_id1");
            Assert.AreEqual(2, result1.Count);
            Assert.AreEqual(true, result1.Contains(_sut.GetBPMNFlowNodeById("_id2")));
            Assert.AreEqual(true, result1.Contains(_sut.GetBPMNFlowNodeById("_id3")));

            var result2 = _sut.GetTargetBPMNFlowNodes("_id2");
            Assert.AreEqual(0, result2.Count);

            var result4 = _sut.GetTargetBPMNFlowNodes("_id3");
            Assert.AreEqual(1, result4.Count);
            Assert.AreEqual(true, result4.Contains(_sut.GetBPMNFlowNodeById("_id1")));
        }

        [TestMethod]
        public void IsAncestorOf_in_case_of_simple_sequential_model()
        {
            CreateSut(new tProcessCtor().Flow("_id1", "_id2").Flow("_id2", "_id3").Flow("_id3", "_id4").Create());

            Assert.AreEqual(true, _sut.IsAncestorOf("_id1", "_id2"));
            Assert.AreEqual(true, _sut.IsAncestorOf("_id1", "_id3"));
            Assert.AreEqual(true, _sut.IsAncestorOf("_id1", "_id4"));
        }

        [TestMethod]
        public void IsAncestorOf_in_case_of_model_having_some_kind_of_branching()
        {
            CreateSut(new tProcessCtor().Flow("_id1", "_id2").Flow("_id1", "_id3").Flow("_id2", "_id4").Flow("_id3", "_id5").Create());

            Assert.AreEqual(true, _sut.IsAncestorOf("_id1", "_id2"));
            Assert.AreEqual(true, _sut.IsAncestorOf("_id1", "_id3"));
            Assert.AreEqual(true, _sut.IsAncestorOf("_id1", "_id4"));
            Assert.AreEqual(true, _sut.IsAncestorOf("_id1", "_id5"));

            Assert.AreEqual(true, _sut.IsAncestorOf("_id2", "_id4"));
            Assert.AreEqual(true, _sut.IsAncestorOf("_id3", "_id5"));

            Assert.AreEqual(false, _sut.IsAncestorOf("_id2", "_id3"));
            Assert.AreEqual(false, _sut.IsAncestorOf("_id3", "_id4"));
        }


        [TestMethod]
        public void Reduce()
        {
            CreateSut(new tProcessCtor()
                .Flow("id1", "id2").Flow("id2", "id3").Flow("id3", "id4").Create());

            var bpmnBlockGraph = new BPMNFlowNodesBlock(new BPMNFlowNodeCtor().WithId("id2").Create());
            var bpmnFlowNode = new BPMNFlowNodeCtor().WithId("id3").Create();
            bpmnBlockGraph.AddFlow(bpmnBlockGraph.FlowNodeIn, bpmnFlowNode);
            bpmnBlockGraph.NodeOut = bpmnFlowNode;

            var actual = _sut.Reduce(bpmnBlockGraph);

            Assert.AreNotSame(_sut, actual);

            Assert.AreEqual(3, actual.NumBPMNFlowNodes);
            Assert.AreEqual(1, actual.GetTargetBPMNFlowNodes("id1").Count);
            Assert.AreEqual(1, actual.GetTargetBPMNFlowNodes(bpmnBlockGraph.Id).Count);
            Assert.AreEqual(0, actual.GetTargetBPMNFlowNodes("id4").Count);

            Assert.AreEqual(actual.GetBPMNFlowNodeById(bpmnBlockGraph.Id), actual.GetTargetBPMNFlowNodes("id1")[0]);
            Assert.AreEqual(actual.GetBPMNFlowNodeById("id4"), actual.GetTargetBPMNFlowNodes(bpmnBlockGraph.Id)[0]);
        }

        [TestMethod]
        public void Reduce_BPMNSequenceFlows_is_not_null()
        {
            CreateSut(new tProcessCtor()
                .Flow("id1", "id2")
                .Flow("id2", "id3")
                .Flow("id3", "id4").Create());

            var bpmnBlockGraph = new BPMNFlowNodesBlock(new BPMNFlowNodeCtor().WithId("id2").Create());
            var bpmnFlowNode = new BPMNFlowNodeCtor().WithId("id3").Create();
            bpmnBlockGraph.AddFlow(bpmnBlockGraph.FlowNodeIn, bpmnFlowNode);
            bpmnBlockGraph.NodeOut = bpmnFlowNode;

            var actual = _sut.Reduce(bpmnBlockGraph);
            Assert.IsNotNull(actual.BPMNSequenceFlows);
        }

        [TestMethod]
        public void Reduce_in_case_of_replacement_is_single_element_replacing_single_element()
        {
            CreateSut(new tProcessCtor().Flow("id1", "id2").Flow("id2", "id3").Create());

            var bpmnFlowNode = new BPMNFlowNodeCtor().WithId("id2").Create();
            var bpmnBlockGraph = new BPMNFlowNodesBlock(bpmnFlowNode);
            bpmnBlockGraph.NodeOut = bpmnFlowNode;

            var actual = _sut.Reduce(bpmnBlockGraph);

            Assert.AreNotSame(_sut, actual);

            Assert.AreEqual(3, actual.NumBPMNFlowNodes);
            Assert.AreEqual(1, actual.GetTargetBPMNFlowNodes("id1").Count);
            Assert.AreEqual(1, actual.GetTargetBPMNFlowNodes(bpmnBlockGraph.Id).Count);
            Assert.AreEqual(0, actual.GetTargetBPMNFlowNodes("id3").Count);

            Assert.AreEqual(actual.GetBPMNFlowNodeById(bpmnBlockGraph.Id), actual.GetTargetBPMNFlowNodes("id1")[0]);
            Assert.AreEqual(actual.GetBPMNFlowNodeById("id3"), actual.GetTargetBPMNFlowNodes(bpmnBlockGraph.Id)[0]);
        }

        [TestMethod]
        public void Reduce_in_case_of_replacement_block_has_no_end_node()
        {
            CreateSut(new tProcessCtor().Flow("id1", "id2").Flow("id2", "id3").Flow("id3", "id4").Create());

            var bpmnBlockGraph = new BPMNFlowNodesBlock(new BPMNFlowNodeCtor().WithId("id2").Create());
            bpmnBlockGraph.AddFlow(bpmnBlockGraph.FlowNodeIn, new BPMNFlowNodeCtor().WithId("id3").Create());
            bpmnBlockGraph.AddFlow(new BPMNFlowNodeCtor().WithId("id3").Create(), new BPMNFlowNodeCtor().WithId("id4").Create());

            var actual = _sut.Reduce(bpmnBlockGraph);

            Assert.AreNotSame(_sut, actual);

            Assert.AreEqual(2, actual.NumBPMNFlowNodes);
            Assert.AreEqual(1, actual.GetTargetBPMNFlowNodes("id1").Count);
            var idsOfNextNodes = actual.GetTargetBPMNFlowNodes(bpmnBlockGraph.Id);
            Assert.AreEqual(0, idsOfNextNodes.Count);

            Assert.AreEqual(actual.GetBPMNFlowNodeById(bpmnBlockGraph.Id), actual.GetTargetBPMNFlowNodes("id1")[0]);
        }

        [TestMethod]
        public void Reduce2()
        {
            CreateSut(new tProcessCtor()
                .Flow("_id1", "_id2")
                .Flow("_id2", "_id_a_branch")
                .Flow("_id2", "_id_b_branch")
                .Flow("_id_a_branch", "_id_end")
                .Flow("_id_b_branch", "_id_end")
                .Flow("_id_end", "_the_real_end")
                .Create());

            var bpmnBlockGraph = new BPMNFlowNodesBlock(new BPMNFlowNodeCtor().WithId("_id2").Create());
            var bpmnFlowNode = new BPMNFlowNodeCtor().WithId("_id_end").Create();
            bpmnBlockGraph.AddFlow(bpmnBlockGraph.FlowNodeIn, bpmnFlowNode);

            bpmnBlockGraph.AddFlow(bpmnBlockGraph.FlowNodeIn, new BPMNFlowNodeCtor().WithId("_id_a_branch").Create());
            bpmnBlockGraph.AddFlow(bpmnBlockGraph.FlowNodeIn, new BPMNFlowNodeCtor().WithId("_id_b_branch").Create());
            bpmnBlockGraph.AddFlow(bpmnBlockGraph.FlowNodeIn, bpmnFlowNode);
            bpmnBlockGraph.NodeOut = bpmnFlowNode;

            var actual = _sut.Reduce(bpmnBlockGraph);

            Assert.AreNotSame(_sut, actual);

            Assert.AreEqual(3, actual.NumBPMNFlowNodes);
            Assert.AreEqual(1, actual.GetTargetBPMNFlowNodes("_id1").Count);
            Assert.AreEqual(1, actual.GetTargetBPMNFlowNodes("block__id2").Count);
            Assert.AreEqual(0, actual.GetTargetBPMNFlowNodes("_the_real_end").Count);

            Assert.AreEqual(actual.GetBPMNFlowNodeById("block__id2"), actual.GetTargetBPMNFlowNodes("_id1")[0]);
            Assert.AreEqual(actual.GetBPMNFlowNodeById("_the_real_end"), actual.GetTargetBPMNFlowNodes("block__id2")[0]);
        }

        [TestMethod]
        public void GetNumberOfIncomingFlows_inco_00_incoming_flow()
        {
            var tProcess = new tProcessCtor().Flow("id1", "id2");
            CreateSut(tProcess.Create());

            Assert.AreEqual(0, _sut.GetNumberOfIncomingFlows(_sut.GetBPMNFlowNodeById("id1")));
        }

        [TestMethod]
        public void GetNumberOfIncomingFlows_inco_01_incoming_flow()
        {
            var tProcess = new tProcessCtor().Flow("id1", "id2");
            CreateSut(tProcess.Create());

            Assert.AreEqual(1, _sut.GetNumberOfIncomingFlows(_sut.GetBPMNFlowNodeById("id2")));
        }

        [TestMethod]
        public void GetNumberOfIncomingFlows_inco_02_incoming_flow()
        {
            var tProcess = new tProcessCtor().Flow("id1", "id3").Flow("id2", "id3");
            CreateSut(tProcess.Create());

            Assert.AreEqual(2, _sut.GetNumberOfIncomingFlows(_sut.GetBPMNFlowNodeById("id3")));
        }

        [TestMethod]
        public void GetNumberOfOutgoingFlows_inco_00_outgoing_flows()
        {
            var tProcess = new tProcessCtor().Flow("id1", "id2").Create();
            CreateSut(tProcess);

            Assert.AreEqual(0, _sut.GetNumberOfOutgoingFlows(_sut.GetBPMNFlowNodeById("id2")));
        }

        [TestMethod]
        public void GetNumberOfOutgoingFlows_inco_01_outgoing_flows()
        {
            var tProcess = new tProcessCtor().Flow("id1", "id2").Create();
            CreateSut(tProcess);

            Assert.AreEqual(1, _sut.GetNumberOfOutgoingFlows(_sut.GetBPMNFlowNodeById("id1")));
        }

        [TestMethod]
        public void GetNumberOfOutgoingFlows_inco_02_outgoing_flows()
        {
            var tProcess = new tProcessCtor()
                .Branch("id1", "id2")
                .Branch("id1", "id3")
                .Create();
            CreateSut(tProcess);

            Assert.AreEqual(2, _sut.GetNumberOfOutgoingFlows(_sut.GetBPMNFlowNodeById("id1")));
        }

        [TestMethod]
        public void GetConvergingFlowNode_inco_2_branches_no_converging_flownode()
        {
            var tProcess = new tProcessCtor()
                .Branch("gw", "b1")
                .Branch("gw", "b2");
            CreateSut(tProcess.Create());

            var actual = _sut.GetConvergingFlowNode(_sut.GetBPMNFlowNodeById("gw"));

            Assert.AreEqual(null, actual);
        }

        [TestMethod]
        public void GetConvergingFlowNode_inco_2_branches_and_converging_flownode()
        {
            var tProcess = new tProcessCtor()
                .Branch("gw", "b1")
                .Branch("gw", "b2")
                .Flow("b1", "conv")
                .Flow("b2", "conv").Create();
            CreateSut(tProcess);

            var actual = _sut.GetConvergingFlowNode(_sut.GetBPMNFlowNodeById("gw"));

            Assert.AreSame(_sut.GetBPMNFlowNodeById("conv"), actual);
        }

        [TestMethod]
        public void GetConvergingFlowNode_inco_3_branches_and_only_2_are_converging()
        {
            var tProcess = new tProcessCtor()
                .Branch("gw", "b1")
                .Branch("gw", "b2")
                .Branch("gw", "b3")
                .Flow("b1", "conv")
                .Flow("b2", "conv").Create();
            CreateSut(tProcess);

            var actual = _sut.GetConvergingFlowNode(_sut.GetBPMNFlowNodeById("gw"));

            Assert.AreSame(_sut.GetBPMNFlowNodeById("conv"), actual);
        }

        [TestMethod]
        public void GetConvergingFlowNode_inco_branching_but_one_branch_flows_immediately_into_converging_node()
        {
            var tProcess = new tProcessCtor()
                        .Flow("s", "g1")
                        .Branch("g1", "u1")
                        .Branch("g1", "g2")
                        .Flow("u1", "g2").Create();
            CreateSut(tProcess);

            var actual = _sut.GetConvergingFlowNode(_sut.GetBPMNFlowNodeById("g1"));
            var expected = _sut.GetBPMNFlowNodeById("g2");
            Assert.IsNotNull(actual);
            Assert.AreSame(expected, actual);
        }

        [TestMethod]
        public void GetConvergingFlowNode_inco_3_branches_and_2_are_converging_and_that_flow_converging_with_3rd_branch()
        {
            var tProcess = new tProcessCtor()
                .Branch("gw", "b1")
                .Branch("gw", "b2")
                .Branch("gw", "b3")
                .Flow("b1", "conv_b1_b2")
                .Flow("b2", "conv_b1_b2")
                .Flow("b3", "conv")
                .Flow("conv_b1_b2", "conv")
                .Create();
            CreateSut(tProcess);

            var actual = _sut.GetConvergingFlowNode(_sut.GetBPMNFlowNodeById("gw"));

            Assert.AreSame(_sut.GetBPMNFlowNodeById("conv"), actual);
        }

        [TestMethod]
        public void GetBPMNSubProcess_inco_2_branches_no_converging_flownode()
        {
            var tProcess = new tProcessCtor()
                .Branch("gw", "b1")
                .Branch("gw", "b2");
            CreateSut(tProcess.Create());

            var actual = _sut.GetBPMNSubProcess(_sut.GetBPMNFlowNodeById("b1"), null);

            Assert.AreNotSame(_sut, actual);
            Assert.AreEqual(1, actual.BPMNFlowNodes.Count);
            Assert.AreSame(_sut.GetBPMNFlowNodeById("b1"), actual.BPMNFlowNodes[0]);
            Assert.AreSame(actual.BPMNFlowNodes[0], actual.FlowNodeIn);
        }

        [TestMethod]
        public void GetBPMNSubProcess_inco_2_branches_and_converging_flownode()
        {
            var tProcess = new tProcessCtor()
                .Branch("gw", "b1")
                .Branch("gw", "b2")
                .Flow("b1", "conv")
                .Flow("b2", "conv").Create();
            CreateSut(tProcess);

            var actual = _sut.GetBPMNSubProcess(_sut.GetBPMNFlowNodeById("b1"), _sut.GetBPMNFlowNodeById("conv"));

            Assert.AreNotSame(_sut, actual);
            Assert.AreEqual(1, actual.BPMNFlowNodes.Count);
            Assert.AreSame(_sut.GetBPMNFlowNodeById("b1"), actual.BPMNFlowNodes[0]);
            Assert.AreSame(actual.BPMNFlowNodes[0], actual.FlowNodeIn);
        }

        [TestMethod]
        public void GetBPMNSubProcess_inco_3_branches_and_only_2_are_converging()
        {
            var tProcess = new tProcessCtor()
                .Branch("gw", "b1")
                .Branch("gw", "b2")
                .Branch("gw", "b3")
                .Flow("b1", "conv")
                .Flow("b2", "conv").Create();
            CreateSut(tProcess);

            var actual = _sut.GetBPMNSubProcess(_sut.GetBPMNFlowNodeById("b3"), _sut.GetBPMNFlowNodeById("conv"));

            Assert.AreNotSame(_sut, actual);
            Assert.AreEqual(1, actual.BPMNFlowNodes.Count);
            Assert.AreSame(_sut.GetBPMNFlowNodeById("b3"), actual.BPMNFlowNodes[0]);
            Assert.AreSame(actual.BPMNFlowNodes[0], actual.FlowNodeIn);
        }

        [TestMethod]
        public void GetBPMNSubProcess_inco_3_branches_and_2_are_converging_and_that_flow_converging_with_3rd_branch()
        {
            var tProcess = new tProcessCtor()
                .Branch("gw", "b1")
                .Branch("gw", "b2")
                .Branch("gw", "b3")
                .Flow("b1", "conv_b1_b2")
                .Flow("b2", "conv_b1_b2")
                .Flow("b3", "conv")
                .Flow("conv_b1_b2", "conv")
                .Create();
            CreateSut(tProcess);

            var actual = _sut.GetBPMNSubProcess(_sut.GetBPMNFlowNodeById("b1"), _sut.GetBPMNFlowNodeById("conv"));

            Assert.AreNotSame(_sut, actual);
            Assert.AreEqual(2, actual.BPMNFlowNodes.Count);
            Assert.AreSame(_sut.GetBPMNFlowNodeById("b1"), actual.BPMNFlowNodes[0]);
            Assert.AreSame(_sut.GetBPMNFlowNodeById("conv_b1_b2"), actual.BPMNFlowNodes[1]);
            Assert.AreSame(actual.BPMNFlowNodes[0], actual.FlowNodeIn);
        }

        [TestMethod]
        public void GetBPMNSubProcess_inco_sequential_flow()
        {
            var tProcess = new tProcessCtor()
                .Flow("b1", "b2")
                .Flow("b2", "b3")
                .Flow("b3", "b4")
                .Create();
            CreateSut(tProcess);

            var actual = _sut.GetBPMNSubProcess(_sut.GetBPMNFlowNodeById("b2"), _sut.GetBPMNFlowNodeById("b4"));

            Assert.AreNotSame(_sut, actual);
            Assert.AreEqual(2, actual.BPMNFlowNodes.Count);
            Assert.AreSame(_sut.GetBPMNFlowNodeById("b2"), actual.BPMNFlowNodes[0]);
            Assert.AreSame(_sut.GetBPMNFlowNodeById("b3"), actual.BPMNFlowNodes[1]);
            Assert.AreSame(actual.BPMNFlowNodes[0], actual.FlowNodeIn);
        }

        [TestMethod]
        public void GetPreviousFlowNode_inco_sequential_flow()
        {
            var tProcess = new tProcessCtor()
                .Flow("b1", "b2")
                .Flow("b2", "b3")
                .Flow("b3", "b4")
                .Create();
            CreateSut(tProcess);

            var b1 = _sut.GetBPMNFlowNodeById("b1");
            var b2 = _sut.GetBPMNFlowNodeById("b2");
            var b3 = _sut.GetBPMNFlowNodeById("b3");
            var b4 = _sut.GetBPMNFlowNodeById("b4");

            Assert.AreSame(b1, _sut.GetPreviousFlowNode(b1, b2));
            Assert.AreSame(b2, _sut.GetPreviousFlowNode(b1, b3));
            Assert.AreSame(b3, _sut.GetPreviousFlowNode(b1, b4));
            Assert.AreSame(b3, _sut.GetPreviousFlowNode(b3, b4));

            // Corner case
            Assert.AreSame(b1, _sut.GetPreviousFlowNode(b1, b2));
        }

        [TestMethod]
        public void GetPreviousFlowNode_inco_diverging_converging()
        {
            var tProcess = new tProcessCtor()
                .Branch("d", "b1")
                .Branch("d", "b2")
                .Flow("b1", "b1a")
                .Flow("b2", "b2a")
                .Flow("b1a", "c")
                .Flow("b2a", "c")
                .Create();
            CreateSut(tProcess);

            var b1 = _sut.GetBPMNFlowNodeById("b1");
            var b2 = _sut.GetBPMNFlowNodeById("b2");
            var b1a = _sut.GetBPMNFlowNodeById("b1a");
            var b2a = _sut.GetBPMNFlowNodeById("b2a");
            var c = _sut.GetBPMNFlowNodeById("c");
            var d = _sut.GetBPMNFlowNodeById("d");

            Assert.AreSame(b1a, _sut.GetPreviousFlowNode(b1, c));
            Assert.AreSame(b2a, _sut.GetPreviousFlowNode(b2, c));

            // Corner case
            Assert.AreSame(d, _sut.GetPreviousFlowNode(d, b1));
        }

        [TestMethod]
        public void GetPreviousFlowNode_inco_diverging_but_not_converging()
        {
            var tProcess = new tProcessCtor()
                .Branch("d", "b1")
                .Branch("d", "b2")
                .Flow("b1", "b1a")
                .Flow("b2", "b2a")
                .Flow("b1a", "c")
                .Create();
            CreateSut(tProcess);

            var b2 = _sut.GetBPMNFlowNodeById("b2");
            var c = _sut.GetBPMNFlowNodeById("c");

            Assert.AreEqual(null, _sut.GetPreviousFlowNode(b2, c));
        }
    }

    internal static class HelperClass
    {
        internal static bool IsAncestorOf(this BPMNFlowNodesBlock self, String idOfParent, String idOfChild)
        {
            return self.IsAncestorOf(self.GetBPMNFlowNodeById(idOfParent), self.GetBPMNFlowNodeById(idOfChild));
        }
    }
}